package com.myk.game.gobangchess.rooms;

import com.myk.game.gobangchess.*;
import com.myk.game.gobangchess.constants.BoardStyleData;
import com.myk.game.gobangchess.constants.WinMode;
import com.myk.game.gobangchess.signs.AroundSign;
import com.myk.game.gobangchess.signs.TextSign;
import com.myk.game.gobangchess.utils.GridsUtils;

import java.awt.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 逆界黑白棋
 */
public class ReversiRoom extends Room {

    //对弈开始前出现，此时不能落子，点击后按钮消失正式开始对弈
    CustomBottom cb_normal_board = new CustomBottom(this, "传统棋盘", (player) -> {
        originGrids = BoardStyleData.TRADITION_REVERSI_STYLE;
        resetGrids();
        noticeBlackMove();
        preHistory(); //准备记录日志
    });

    //对弈开始开始前出现，此时不能落子，点击后按钮消失正式开始对弈
    CustomBottom cb_octagon_board = new CustomBottom(this, "八边棋盘", (player) -> {
        originGrids = BoardStyleData.OCTAGON_REVERSI_STYLE;
        resetGrids();
        noticeBlackMove();
        preHistory(); //准备记录日志
    });

    //刚开时出现，落子后消失，点击后能够再点
    CustomBottom cb_balance = new CustomBottom(this, "天弈开局", (player) -> {
        originGrids = BoardStyleData.DEFAULT_REVERSI_BIG_STYLE;
        curGrids = getNiJieStyle();
        updateScores(); //每次刷新重置棋盘都要重新算分数
        noticeBlackMove();
        preHistory(); //准备记录日志
    });

    private List<Integer> signIds = new ArrayList<>();

    public ReversiRoom(String roomName) {
        super(roomName, "\t\t黑白棋以及一些特殊棋盘的黑白棋。","逆界黑白棋", BoardStyleData.TRADITION_REVERSI_STYLE, true, true);
    }

    //圣留样式，要随机筑墙
    private int[][] getNiJieStyle() {
        //先定义标大号的准模板
        int[][] style = {
                {9, 0, 0, 0, 0, 0, 0, 0, 0, 9},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 1, 2, 0, 0, 0, 0},
                {0, 0, 0, 0, 2, 1, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {9, 0, 0, 0, 0, 0, 0, 0, 0, 9}
        };

        //随机得到的4个筑墙点，分别是四个对角的区域，不会是已有墙或棋子的地方筑墙，（区域划分采取的硬编码）
        Point top_left = GridsUtils.gridsBuildWall(new Point(0, 0), new Point(4, 4), new Point(0, 0), new Point(4, 4));
        Point top_right = GridsUtils.gridsBuildWall(new Point(5, 0), new Point(9, 4), new Point(5, 4), new Point(9, 0));
        Point bottom_Left = GridsUtils.gridsBuildWall(new Point(0, 5), new Point(4, 9), new Point(0, 9), new Point(4, 5));
        Point bottom_right = GridsUtils.gridsBuildWall(new Point(5, 5), new Point(9, 9), new Point(5, 5), new Point(9, 9));
        //把标准模板筑墙
        style[top_left.y][top_left.x] = 9;
        style[top_right.y][top_right.x] = 9;
        style[bottom_Left.y][bottom_Left.x] = 9;
        style[bottom_right.y][bottom_right.x] = 9;

        return style;
    }

    @Override
    public String getRuleText() {
        return "传统黑白棋以及两个小变体\n" +
                "八边黑白棋只是扩展了棋盘大小和形状\n" +
                "而天弈黑白棋是在经典黑白棋的基础上扩大棋盘，\n" +
                "并由电脑设置一共八个障碍。\n" +
                "落子后，刚落下的棋子和另一颗棋子夹住的对方棋子变成己方棋子。\n" +
                "\n" +
                "获胜条件：终盘时，棋子数量高于对方\n" +
                "和棋条件：中盘时，双方棋子数量一致";
    }

    @Override
    protected void doStart() {
        overFlag = 1;//重置结束状态：对弈进行中未结束
        playingStartTime = System.currentTimeMillis(); //重置对弈开始时间
        resetGrids();
        noticeNoMove();
        if (player_black != null) {
            player_black.registerCustomBottom(cb_normal_board, cb_octagon_board, cb_balance);
        }
        if (player_white != null) {
            player_white.registerCustomBottom(cb_normal_board, cb_octagon_board, cb_balance);
        }
    }


    private void clearCustoms() {
        if (player_black != null) {
            if (player_black.getCustomBottomList(this).size() > 0) {
                player_black.clearCustomBottom();
                player_black.dispatchCustomBottom();
            }
        }
        if (player_white != null) {
            if (player_white.getCustomBottomList(this).size() > 0) {
                player_white.clearCustomBottom();
                player_white.dispatchCustomBottom();
            }
        }
    }

    @Override
    public void resetGrids() {
        super.resetGrids();
        signIds.clear();
        updateScores(); //每次刷新重置棋盘都要重新算分数
    }

    @Override
    public void doRule(int row, int col, int value) {
        clearCustoms();
        if (value == 1) {
            if (ifMovePicesSeccess(row, col, value)) { //黑棋落子完成后
                updateScores(); //成功落子就修改分数
                //先判断白棋是否无路可走
                if (checkGameOver(2)) {
                    //如果白棋无路可走，再判断黑棋是否也是无路可走
                    if (checkGameOver(1)) {
                        //白棋黑棋都无路可走，那么有戏结束，去判断胜负吧
                        onGameOver();
                    }
                    else {
                        //黑棋继续走
                        stepAdd();;
                        noticeBlackMove();
                    }
                }
                else {
                    //提示白棋落子
                    stepAdd();;
                    noticeWhiteMove();
                }
            }
        }
        if (value == 2) {
            if (ifMovePicesSeccess(row, col, value)) { //白棋落子完成后
                updateScores();  //成功落子就修改分数
                noticeAllRefreshGameInfo();
                //先判断黑棋是否无路可走
                if (checkGameOver(1)) {
                    if (checkGameOver(2)) {
                        onGameOver();
                    }
                    else {
                        stepAdd();;
                        noticeWhiteMove();
                    }
                }
                else {
                    //提示黑棋落子
                    stepAdd();;
                    noticeBlackMove();
                }
            }
        }
    }

    //修改分数
    private void updateScores() {
        blackScore = GridsUtils.getDataCount(curGrids, 1);
        whiteScore = GridsUtils.getDataCount(curGrids, 2);
    }

    //执行落子操作和判断落子是否成功
    private boolean ifMovePicesSeccess(int row, int col, int value) {
        if (curGrids[row][col] != 0) { //如果该位置不为空不能落子
            return false;
        }
        List<List<int[]>> usefulDirList = getUsefulEightDirGridsLineList(row, col, value); //有效的八个方向的棋子链
        if (usefulDirList.size() == 0) {
            return false;
        }
        boolean isHasOverturn = false;
        for (List<int[]> list : usefulDirList) {
            //遍历所有有效的链表，执行翻转棋子，如果能翻转记个数
            if (ifOverturnSuccess(row, col, value, list)) {
                isHasOverturn = true;
            }
        }
        if (isHasOverturn) {
            signList.clear();
            signList.add(new TextSign(getIndex(row, col), "※", "#FFDDAA33"));
            signIds.add(getIndex(row, col));
            signList.add(new AroundSign(new ArrayList<>(signIds), "#6600FF00", "#9900ff00", 1.2f));
            signIds.clear();
        }
        return isHasOverturn;
    }


    //棋子翻转功能
    private boolean ifOverturnSuccess(int row, int col, int value, List<int[]> list) {
        List<int[]> tempCanOverList = new ArrayList<>(); //临时保存预计可翻的棋子列表
        boolean canOver = false; //这组列表是否可翻
        for (int[] i : list) {
            if (i[2] != value) {
                if (i[2] != 0 && i[2] != 9) { //别忘了空白和墙的情况要排除
                    tempCanOverList.add(i); //颜色不相同的加进临时可翻棋子列表
                }
                else {
                    break;
                }
            }
            else {
                canOver = true; //一旦遇到相同的列表，那么就中断遍历开始翻转，因为之前数据验证过，所以这里不用对第一个格子进行校验
                break;
            }
        }
        if (canOver) {
            curGrids[row][col] = value; //落下棋子
            for (int[] can : tempCanOverList) {
                curGrids[can[0]][can[1]] = value; //翻转棋子
            }
            addOverSigns(tempCanOverList); //更新标记
        }
        return canOver;
    }

    private void addOverSigns(List<int[]> tempCanOverList) {
        List<Integer> ids = new ArrayList<>();
        for (int[] can : tempCanOverList) {
            ids.add(getIndex(can[0], can[1]));
        }
        signIds.addAll(ids);
    }

    //判断是否无路可走，value=5判断是否黑棋无路可走，value=6判断是否白棋无路可走
    private boolean checkGameOver(int value) {
        int row = curGrids.length;
        int col = curGrids[0].length;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (thisPointCanMove(i, j, value)) {
                    //一旦有能下value棋的地方，就返回false
                    return false;
                }

            }
        }
        return true; //所有格子都不能走游戏结束
    }

    //当游戏结束时，各种操作
    private void onGameOver() {
        if (blackScore > whiteScore) {
            String blackWinMsg = player_black.getNickNameOrTemp() + "执黑棋战胜" + player_white.getNickNameOrTemp();
            doOver(WinMode.BLACK_WIN, blackWinMsg);
        }
        else if (whiteScore > blackScore) {
            String whiteWinMsg = player_white.getNickNameOrTemp() + "执白棋战胜" + player_black.getNickNameOrTemp();
            doOver(WinMode.WHITE_WIN, whiteWinMsg);
        }
        else {
            String bothWinMsg = player_black.getNickNameOrTemp() + "-黑棋战平" + player_white.getNickNameOrTemp() + "-白棋";
            doOver(WinMode.BOTH_DRAW, bothWinMsg);
        }
    }

    //执行落子操作和判断落子是否成功
    private boolean thisPointCanMove(int row, int col, int value) {
        if (curGrids[row][col] != 0) { //如果该位置不为空不能落子
            return false;
        }
        List<List<int[]>> usefulDirList = getUsefulEightDirGridsLineList(row, col, value); //有效的八个方向的棋子链
        if (usefulDirList.size() == 0) {
            return false;
        }
        boolean isHasOverturn = false;
        for (List<int[]> list : usefulDirList) {
            //遍历所有有效的链表，判断是否能翻转，如果能翻转记个数（只是判断，不实际做翻转的动作）
            if (thisPointCanOverTurnList(row, col, value, list)) {
                isHasOverturn = true;
            }
        }
        return isHasOverturn;
    }

    //获取当前位置格子的八个方向到边的格子链，顺序为从上开始逆时针知道右上。
    private List<List<int[]>> getUsefulEightDirGridsLineList(int row, int col, int value) {
        int boardRows = curGrids.length; //棋盘总行数
        int boardCols = curGrids[0].length; //棋盘总列数
        List<List<int[]>> eightDirList = new ArrayList<>(); //八方向的链表
        //上
        List<int[]> topList = new ArrayList<>(); //往上走的链表
        int topLen = row; //距离边的格数
        for (int i = 1; i <= topLen; i++) {
            int nRow = row - i;
            int nCol = col;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            topList.add(g);
        }
        eightDirList.add(topList);
        //左上
        List<int[]> leftTopList = new ArrayList<>();
        int leftTopLen = Math.min(row, col);
        for (int i = 1; i <= leftTopLen; i++) {
            int nRow = row - i;
            int nCol = col - i;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            leftTopList.add(g);
        }
        eightDirList.add(leftTopList);
        //左
        List<int[]> leftList = new ArrayList<>();
        int leftLen = col;
        for (int i = 1; i <= leftLen; i++) {
            int nRow = row;
            int nCol = col - i;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            leftList.add(g);
        }
        eightDirList.add(leftList);
        //左下
        List<int[]> leftBottomList = new ArrayList<>();
        int leftBottomLen = Math.min(boardRows - (row + 1), col);
        for (int i = 1; i <= leftBottomLen; i++) {
            int nRow = row + i;
            int nCol = col - i;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            leftBottomList.add(g);
        }
        eightDirList.add(leftBottomList);
        //下
        List<int[]> bottomList = new ArrayList<>(); //往上走的链表
        int bottomLen = boardRows - (row + 1);
        for (int i = 1; i <= bottomLen; i++) {
            int nRow = row + i;
            int nCol = col;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            bottomList.add(g);
        }
        eightDirList.add(bottomList);
        //右下
        List<int[]> rightBottomList = new ArrayList<>();
        int rightBottomLen = Math.min(boardRows - (row + 1), boardCols - (col + 1));
        for (int i = 1; i <= rightBottomLen; i++) {
            int nRow = row + i;
            int nCol = col + i;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            rightBottomList.add(g);
        }
        eightDirList.add(rightBottomList);
        //右
        List<int[]> rightList = new ArrayList<>();
        int rightLen = boardCols - (col + 1);
        for (int i = 1; i <= rightLen; i++) {
            int nRow = row;
            int nCol = col + i;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            rightList.add(g);
        }
        eightDirList.add(rightList);
        //右上
        List<int[]> rightTopList = new ArrayList<>();
        int rightTopLen = Math.min(row, boardCols - (col + 1));
        for (int i = 1; i <= rightTopLen; i++) {
            int nRow = row - i;
            int nCol = col + i;
            int[] g = {nRow, nCol, curGrids[nRow][nCol]};
            rightTopList.add(g);
        }
        eightDirList.add(rightTopList);

        //把不符合要求的淘汰掉
        List<List<int[]>> useLessList = new ArrayList<>();
        for (List<int[]> list : eightDirList) {
            if (list.size() <= 1) {//链表至少有两个格子才可能形成夹棋
                useLessList.add(list);
            }
            else if (list.get(0)[2] == value) { //相邻的格子得为异色
                useLessList.add(list);
            }
        }
        for (List<int[]> list : useLessList) {
            eightDirList.remove(list);
        }

        return eightDirList;
    }

    //判断若下在该位置，该列是否有棋子能翻转，仅在 thisPointCanMove() 中用
    private boolean thisPointCanOverTurnList(int row, int col, int value, List<int[]> list) {
        List<int[]> tempCanOverList = new ArrayList<>(); //临时保存预计可翻的棋子列表
        boolean canOver = false; //这组列表是否可翻
        for (int[] i : list) {
            if (i[2] != value) {
                if (i[2] != 0 && i[2] != 9) { //别忘了空白和墙的情况要排除
                    tempCanOverList.add(i); //颜色不相同的加进临时可翻棋子列表
                }
                else {
                    break;
                }
            }
            else {
                canOver = true; //一旦遇到相同的列表，那么就中断遍历开始翻转，因为之前数据验证过，所以这里不用对第一个格子进行校验
                break;
            }
        }
        return canOver;
    }

}
